/*
 * Copyright 2008-2023 dexian.vip. All rights reserved.
 * Support: http://www.dexian.vip
 * License: http://www.dexian.vip/license
 */

package vip.dexian.core.base.bean;

import jakarta.persistence.Column;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.Transient;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.CompareToBuilder;

import java.io.Serial;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * 无限级分类基类
 *
 * @author 挺好的 2023年05月31日 9:23
 */
@MappedSuperclass
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@EqualsAndHashCode (callSuper = true)
public abstract class TreeEntity<ID extends Serializable> extends OrderEntity <ID> {

    @Serial
    private static final long serialVersionUID = 1339787613966988928L;

    /**
     * 分隔符
     */
    private static final String SEPARATOR = ",";

    /**
     * 路径，保存上下级关系
     */
    @Column (nullable = false)
    private String treePath;

    /**
     * 层级。顶级为0，子级递增，以此类推
     */
    @Column (nullable = false)
    private Integer grade;

    /**
     * 获取上级ID列表
     *
     * @return 上级ID
     */
    @Transient
    public Long[] getParentIds () {
        String[] parentIds = StringUtils.split(this.getTreePath(), SEPARATOR);
        Long[] result = new Long[parentIds.length];
        for (int i = 0; i < parentIds.length; i++) {
            result[i] = Long.valueOf(parentIds[i]);
        }
        return result;
    }

    /**
     * 排序
     *
     * @param entities
     *         实体类
     * @param <T>
     *         TreeEntity子类
     */
    @Transient
    public static <T extends TreeEntity> void sort (List <T> entities) {
        if (CollectionUtils.isEmpty(entities)) {
            return;
        }
        final Map <Long, Integer> orderMap = new HashMap <>();
        for (T entity : entities) {
            orderMap.put((Long) entity.getId(), entity.getOrder());
        }
        entities.sort((entity1, entity2) -> {
            Long[] ids1 = (Long[]) ArrayUtils.add(entity1.getParentIds(), entity1.getId());
            Long[] ids2 = (Long[]) ArrayUtils.add(entity2.getParentIds(), entity2.getId());
            Iterator <Long> iterator1 = Arrays.asList(ids1).iterator();
            Iterator <Long> iterator2 = Arrays.asList(ids2).iterator();
            CompareToBuilder compareToBuilder = new CompareToBuilder();
            while (iterator1.hasNext() && iterator2.hasNext()) {
                Long id1 = iterator1.next();
                Long id2 = iterator2.next();
                Integer order1 = orderMap.get(id1);
                Integer order2 = orderMap.get(id2);
                compareToBuilder.append(order1, order2).append(id1, id2);
                if (!iterator1.hasNext() || !iterator2.hasNext()) {
                    compareToBuilder.append(entity1.getGrade(), entity2.getGrade());
                }
            }
            return compareToBuilder.toComparison();
        });
    }
}
